﻿//#define _CRT_SECURE_NO_WARNINGS
//#include <stdio.h>
//#include <string.h>
//#include <stdbool.h>
//#include <math.h>

// 注意：这个项目配置启动项目之后需要先清理后运行才行，否则可能报错，因为静态库预处理语句的原因

#include "common.h" // 导入头文件 -- 声明的函数.h

// www.cplusplus.com 是 C++ 学习的一个外文网站
// www.en.cppreference.com 是 C++ 参考手册的英文版（推荐使用）
// www.zh.cppreference.com 是 C++ 参考手册的中文版


 /*1.strcpy() 函数的作用是复制字符串，头文件为 <string.h>，它的原型是：
 char *strcpy(char *dest, const char *src);
 表示将字符串 src 复制到字符串 dest 中，并返回 dest值。*/

 /* 2.strcat() 函数的作用是连接两个字符串，头文件为 <string.h>，它的原型是：
 char *strcat(char *dest, const char *src);
 表示将字符串 src 连接到字符串 dest 的尾部，并返回 dest值。*/

 /* 3.momset() 函数的作用是设置字符串的内存，头文件为 <string.h>，它的原型是：
 void *memset(void *s, int c, size_t count); s 是指针，可以偏移
 表示将字符串 s 中的 count 个字符设置为字符 c，并返回 s 值。*/

float max_num(float a, float b)
{ // 函数定义
    return a > b ? a : b;
}

// 导入静态库的方法：这里导入 static_compute_lib.lib 静态库，注意没有分号
// 注意这个静态库需要放到工程目录下，否则编译会报错
#include "compute.h" // 导入 static_compute_lib.lib 的头文件 compute.h
#pragma comment(lib, "static_compute_lib.lib")


int main(int argc, char* argv[], char* envp[])
// 以上是 main 函数的原型，其中 argc 表示命令行参数的个数，
// argv 表示命令行参数的数组，envp 表示环境变量的数组
{
    // 库函数学习部分
    {
        print_start_line("库函数学习部分开始");
        char str1[100] = "Hello, world!";
        char str2[100];
        strcpy(str2, str1); // 将 str1 复制到 str2 中
        printf("str1 = %s\n", str1);
        printf("str2 = %s\n", str2);
        strcat(str2, " How are you?");
        // 将 " How are you?" 连接到 str2 的尾部
        printf("str2 = %s\n", str2);
        memset(str2, '*', 10); // 将 str2 中的前 10 个字符设置为 *
        memset(str2 + 12, 'x', 5); // 将 str2 中的第 13-17 个字符设置为 x
        printf("str2 = %s\n", str2);

        print_end_line("库函数学习部分结束");
    }

    // 自定义函数学习部分
    {
        print_start_line("自定义函数学习部分开始");
        float num1 = 3.14f, num2 = 2.71f;
        //int num3 = scanf("%f %f", &num1, &num2); // 从控制台读取两个浮点数
        printf("max_num(%.2f, %.2f) = %.6f\n",// 调用函数 max_num() 并输出结果
            num1, num2, max_num(num1, num2));

        printf("square_root(%.1f) = %.6lf\n", 20.0f, square_root(20.0));
        // 调用函数 square_root() 并输出结果

        // 函数实现两个数值交换
        float a = 1.0f, b = 2.0f;
        //float* p1 = &a, *p2 = &b; // 指针 p1 和 p2 指向 a 和 b 的地址
        swap(&a, &b); // 调用函数 swap() 交换 a 和 b 的值

        // 打印 100 到 200 之间的素数
        prime_numbers(100, 200); // 调用函数 prime_numbers() 并返回指针
        // 打印 2050 到 2100 之间的闰年
        printf("There are %d leap years between 2050 and 2100.\n",
            leap_years(2025, 2100)); // 调用函数 leap_years() 并返回计数

        // 函数实现二分查找
        // 生产一个 1 ~ 1000 的连续整数数组
        int array_sort[1000];
        for (int i = 0; i < 1000; i++)
            array_sort[i] = i + 1;
        // 输出数组
        // for (int i = 0; i < 1000; i++) {
        //     printf("%d ", array_sort[i]);
        // }
        // printf("\n");
        // 调用函数 binary_search() 进行二分查找
        int key = 100; // 要查找的数字
        int left = 0, right = sizeof(array_sort) / sizeof(array_sort[0]) - 1;
        // 数组的左右边界
        binary_search(  // 调用函数 binary_search()
            array_sort, left, right, 100);
        // 输出结果

        // 布尔类型变量简单学习
        bool flag = true; // 布尔变量，需要头文件 <stdbool.h>
        printf("flag = %d\n", flag); // 输出 flag 的值
        printf("sizeof(bool) = %zd\n", sizeof(bool)); // 布尔变量的大小

        // 每次调用 add_num() 都会返回 num 的值加 1
        int num = 10;
        for (int i = 0; i < 5; ++i) // 调用函数 add_num() 并输出结果
            printf("add_add_num(&num) = %d\n", add_add_num(&num)); // 输出 num 的值
        // 输出结果为 11 12 13 14 15
        // 一个建议：少写全局变量，在函数中不要修改全局变量，尽量使用指针参数传递变量

        print_end_line("自定义函数学习部分结束");
    }

    // 函数的嵌套学习部分
    {
        print_start_line("函数的嵌套学习部分开始");
        //printf("hello, world! length = %ld\n", strlen("hello, world!"));
        printf("%d\n", printf("%d\t", printf("%d\t", // 链式访问（嵌套printf）
            printf("%d\t", printf("%d\t", 10)))));  // 输出 10 3 2 2 2
        nest(); // 调用函数 nest()

        print_end_line("函数的嵌套学习部分结束");
    }

    // 函数的声明与定义学习部分
    {
        print_start_line("函数的声明与定义学习部分开始");
        // 调用函数在声明函数之前时，需要在调用之前声明，函数的声明一般放在头文件里
        float test_average(float, float); // 声明函数，可以只是指明参数类型
        printf("test_average(10.58, 20.32) = %.2f\n",
            test_average(10.58f, 20.32f));

        // 使用静态库的函数，必须包含头文件和静态库文件，否则编译会报错
        printf("static_lib_add(10, 20) = %f\n", static_lib_add(10.0f, 20.0f));

        print_end_line("函数的声明与定义学习部分结束");
    }

    // 函数的递归学习部分
    {
        print_start_line("函数的递归学习部分开始");
        int num1 = 123456789;
        //int rs = scanf("%d", &num1);
        reverse1(num1); // 调用函数 reverse1() 并输出结果
        printf("\n");
        reverse2(num1);
        printf("\n");  // 调用函数 reverse2() 并输出结果
        char test_str[] = "我不是萝莉控，只是喜欢的人刚好是萝莉而已"; // 40
        // 说明：中文和中文的符号都占两个字符
        printf("str_len1(test_str) = %d\n", str_len1(test_str));
        printf("str_len2(test_str) = %d\n", str_len2(test_str));

        // 阶乘
        int ten_result[2] = { factorial1(10)[0], factorial1(10)[1] };
        printf("factorial1(10)[0] = %d\nfactorial1(10)[1] = %d\n",
            ten_result[0], ten_result[1]);
        printf("factorial2(5) = %d\n", factorial2(10));

        // 斐波那契数列
        fibonacci1(40);
        printf("fibonacci2(40) = %d\n", fibonacci2(40));

        print_end_line("函数的递归学习部分开始");
    }

    // 函数的可变参数学习部分
    {
        print_start_line("函数的可变参数学习部分开始");
        test_va_args(); // 调用函数 test_var_args()
        print_end_line("函数的可变参数学习部分结束");
    }

    // 作业部分
    {
        print_start_line("作业部分开始");
        //求10个数的最大值
        float array_num[10] =
        { 1.1, 29.2, 13.3, 4.4, 10.1, 53.5, 6.6, 7.7, 8.8, 29.9 };
        /* for(int num_max = 0; num_max < 10; ++num_max)
            scanf("%f", &array_num[num_max]); */
        printf("max_all_num(array_num, 10) = %.2f\n",
            max_all_num(array_num, 10));

        // 打印乘法口诀表
        print_multiplication_table(9);

        // 写一个函数求 n 的阶乘，打印 1~10 的阶乘
        factorial_array(10);

        // 模拟实现 strlen 函数, 要求时间复杂度为 O(n)
        char str_test[] = "萝莉控不是病，而是一种生活方式！";
        printf("字符串 str_test 长度为：%d\n", strlen_test1(str_test));
        printf("字符串 str_test 长度为：%d\n", strlen_test2(str_test));

        // 字符串逆序打印，非递归与递归实现
        char str_reverse_test[] = "abcdefghijklmnopqrstuvwxyz";
        reverse_str1(str_reverse_test);
        printf("字符串 str_reverse_test 逆序一次后为：%s\n",
            str_reverse_test);
        reverse_str2(str_reverse_test, 0,
            strlen(str_reverse_test) - 1); // 注意下标位
        printf("字符串 str_reverse_test 逆序两次后为：%s\n",
            str_reverse_test);
        reverse_str3(str_reverse_test);
        printf("字符串 str_reverse_test 逆序三次后为：%s\n",
            str_reverse_test);

        // 计算一个整数的每位数的和（递归实现）
        unsigned int num_sum = 123456789;
        printf("整数 %d 的每位只和为：%d\n", num_sum, digit_sum(num_sum));

        // 递归实现整数 n 的整数 k 次幂
        printf("3 的 9 次幂为：%d\n", power_int(3, 9));
        printf("5.6 的 -3 次幂为：%lf\n", power_double(5.6, -3));
        // 库函数 pow 也可以求浮点数的浮动数次幂，标头为 #include <math.h>
        printf("2.5 的 3.2 次幂为：%lf\n", pow(2.5, 3.2));

        print_end_line("作业部分结束");
    }

    // 测试小数转分数
    {
        printf("测试小数转分数：\n");
        double decimal = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
        test_decimal_to_fraction(decimal);
    }

    return 0;
}

float test_average(float a, float b)
{
    float average = (a + b) / 2.0f;
    return average;
}

// 编译静态库：
/*
可以右键项目打开属性，选择常规，配置类型选择静态库，然后编译，
    会生成 .lib 文件

    1. 打开项目属性，选择 C/C++ 标签，
        在 Additional Include Directories 中添加头文件所在目录，应用并确定

    2. 右键选择项目，选择属性，选择 Linker 标签，
        在 Input 中选择 Additional Dependencies 中添加静态库文件名
        （带 lib 前缀和 .lib 后缀）

    3. 打开命令行，切换到项目目录，输入命令：
        cl /c study03_函数的嵌套.c
        link /DLL /OUT:study03_函数的嵌套.dll study03_函数的嵌套.obj

    4. 编译成功后，在项目目录下会生成 study03_函数的嵌套.dll 文件，
        这个文件可以将其拷贝到其他项目中使用。
*/

/*
内存可分为三个区域：
    1.栈区：存放函数的参数、局部变量、返回地址等信息，函数调用结束后自动释放。
    2.堆区：存放动态分配的内存，程序员手动释放，
    （如malloc、new、realloc、strdup、bcopy、strcpy、strcat、free等函数）
    3.静态区：存放全局变量、静态变量、常量等信息，程序运行前就已经分配好内存。
*/

// 栈区内存的使用一般是先使用高地址，后使用低地址，先释放地地址，后释放高地址。
